﻿using System.IO;
using System.Linq;

using Ionic.Zip;

namespace System.AutoUpdate.Library
{
    /// <summary>
    /// Represents an update package to be executed
    /// </summary>
    public class UpdatePackage
    {
        public string Location
        {
            get;
            private set;
        }

        public string TemporaryDirectory
        {
            get;
            private set;
        }

        public string RollbackDirectory
        {
            get;
            private set;
        }

        public UpdatePackage (string tempDirectory)
        {
            TemporaryDirectory = tempDirectory;
            Location = CreatePackageDirectory ("update");
            RollbackDirectory = CreatePackageDirectory ("backup");
        }

        private string CreatePackageDirectory (string id)
        {
            string directory = id + "_" + DateTime.Now.Year + "-" + DateTime.Now.Month + "-" + DateTime.Now.Day + "pkg_1";

            int count = 2;

            while (Directory.Exists(TemporaryDirectory + directory))
            {
                directory = id + "_" + DateTime.Now.Year + "-" + DateTime.Now.Month + "-" + DateTime.Now.Day + "pkg_" + count;

                count++;
            }

            Directory.CreateDirectory(TemporaryDirectory + directory);

            return directory;
        }

        /// <summary>
        /// Function extracts ZIP file and removes the zip afterwards
        /// </summary>
        /// <param name="file"></param>
        /// <param name="clean"></param>
        public void Extract (string file, bool clean = true)
        {
            if (File.Exists (file))
            {
                //
                // Extract files
                //

                using (ZipFile patch = ZipFile.Read (file))
                {
                    patch.ExtractAll (Location, ExtractExistingFileAction.DoNotOverwrite);
                }

                if (clean)
                {
                    File.Delete (file);
                }
            }
        }

        /// <summary>
        /// Creates a ZIP based update package
        /// </summary>
        public void Prepare ()
        {
            ZipFile zip = new ZipFile();
            zip.AddDirectory(TemporaryDirectory + Location);
            zip.Save(TemporaryDirectory + Location + ".aup");

            //
            // Delete package directory, as we use a zip stuff
            //

            Directory.Delete(TemporaryDirectory + Location, true);
        }

        /// <summary>
        /// Prepares rollback zip
        /// </summary>
        /// <param name="originalDir"></param>
        public void PrepareRollback (string originalDir)
        {
            CopyRollbackFolder (originalDir, Location);

            //
            // Creates zip of original files to get more space
            //

            ZipFile zip = new ZipFile();
            zip.AddDirectory (TemporaryDirectory + RollbackDirectory);
            zip.Save(TemporaryDirectory + RollbackDirectory + ".zip");

            //
            // Delete backup directory
            //

            Directory.Delete (TemporaryDirectory + RollbackDirectory, true);
        }

        /// <summary>
        /// Copies all original files to a backup folder (recursively
        /// </summary>
        private void CopyRollbackFolder (string originalDir, string packageDir)
        {
            //
            // First reads the directories that were added or updated
            //

            string[] updates = Directory.GetDirectories (packageDir);

            foreach (string directory in updates)
            {
                if (Directory.Exists (originalDir + directory))
                {
                    //
                    // Create directory in backup if it was not created yet
                    //

                    if (!Directory.Exists (RollbackDirectory + directory))
                    {
                        Directory.CreateDirectory (RollbackDirectory + directory);
                    }

                    //
                    // Run function recursively
                    //

                    CopyRollbackFolder(originalDir + directory, packageDir + directory);
                }
            }

            //
            // Read all files in package
            //

            updates = Directory.GetFiles (packageDir);

            foreach (string file in updates.Where (file => File.Exists (originalDir + file)))
            {
                File.Copy (originalDir + file, RollbackDirectory + file);
            }

        }
    }
}
